package wordit.impl.boardwise;

import java.util.List;

import wordit.Game;
import wordit.Letter;
import wordit.ea.CrossoverFunction;
import wordit.ea.Genome;

public class BoardwiseCrossover implements CrossoverFunction {

	static private final BoardPartitioner partitioner = new BoardPartitioner();
	private final List<Letter> allLetters;

	public BoardwiseCrossover(Game game) {
		allLetters = game.getLetters();
	}

	@Override
	public Genome[] crossGenoms(Genome parent1genome, Genome parent2genome) {
		BoardGenome parent1 = (BoardGenome) parent1genome;
		BoardGenome parent2 = (BoardGenome) parent2genome;
		BoardGenome tmp;
		final int boardRowsCount = parent1.getRowsCount();
		final int boardColumnsCount = parent1.getColumnsCount();
		BoardGenome offspring1 = new BoardGenome(boardRowsCount,
				boardColumnsCount);
		BoardGenome offspring2 = new BoardGenome(boardRowsCount,
				boardColumnsCount);

		Iterable<Iterable<Letter>> partitions = partitioner
				.createPartitioning(allLetters);

		for (Iterable<Letter> part : partitions) {
			for (Letter l : part) {
				inheritLocation(offspring1, l, parent1, parent2);
				inheritLocation(offspring2, l, parent2, parent1);
			}

			// switch parent1 <-> parent2
			tmp = parent1;
			parent1 = parent2;
			parent2 = tmp;
		}
		
		placeUnusedLetters(offspring1);
		placeUnusedLetters(offspring2);

		Validator.validate(offspring1);
		Validator.validate(offspring2);
		return new Genome[] { offspring1, offspring2 };
	}

	private void placeUnusedLetters(BoardGenome board) {
		for (Letter l : board.getUnusedLetters()) {
			List<BoardLocation> vacantLocations = board.getVacantLocations();
			BoardLocation loc = vacantLocations.get((int) (Math.random()*vacantLocations.size()));
			
			board.setLetterAt(loc.getRow(), loc.getColumn(), l);
		}
	}

	/**
	 * Inherits the location of `l` from `parent`. If this location is already
	 * taken (already inherited from `otherParent`), decides on a dominant
	 * parent and inherits from the dominant parent. The letter inherited from
	 * the recessive (non-dominant) is put aside (its location is set to null).
	 * 
	 * @param offspring
	 * @param l
	 * @param parent
	 * @param otherParent
	 */
	private void inheritLocation(BoardGenome offspring, Letter l,
			BoardGenome parent, BoardGenome otherParent) {

		BoardLocation location;
		Letter takenBy;

		location = parent.getLocationOf(l);
		takenBy = offspring
				.getLetterAt(location.getRow(), location.getColumn());

		if (takenBy == null) {
			offspring.setLetterAt(location.getRow(), location.getColumn(), l);
		} else {
			// a letter was already placed at the location. It now competes with
			// the letter `l`. One of them (the dominant) will be placed at
			// the location, while the other will be put aside.

			BoardGenome dominantParent, recessiveParent;
			Letter dominantLetter, recessiveLetter;

			dominantParent = getDominantParent(location, parent, otherParent);
			recessiveParent = dominantParent == parent ? otherParent : parent;

			dominantLetter = dominantParent.getLetterAt(location.getRow(),
					location.getColumn());
			recessiveLetter = recessiveParent.getLetterAt(location.getRow(),
					location.getColumn());
			
			offspring.setLetterAt(location.getRow(), location.getColumn(),
					dominantLetter);
			offspring.setUnusedLetter(recessiveLetter);
		}
	}

	/**
	 * Decides which of `parent1` and `parent2` should be the dominant for the
	 * location `loc`
	 * 
	 * @param parent1
	 * @param parent2
	 * @param loc
	 * @return either `parent1` or `parent2`
	 */
	private BoardGenome getDominantParent(BoardLocation loc,
			BoardGenome parent1, BoardGenome parent2) {

		return (Math.random() < 0.5) ? parent1 : parent2;
	}

}
